﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using l1cs.server.model.Instance;
using l1cs.server.model.skill;
using l1cs.server.server.types;

namespace l1cs.server.model
{
    public class HpRegeneration
    {
        private static Logger _log = Logger.getLogger( typeof( HpRegeneration ).FullName );

        private L1PcInstance _pc;

        private int _regenMax = 0;

        private int _regenPoint = 0;

        private int _curPoint = 4;

        private static Random _random = new Random();

        // 特別
        public void cancel() { }

        public HpRegeneration( L1PcInstance pc )
        {
            _pc = pc;

            updateLevel();
        }

        public void setState( int state )
        {
            if ( _curPoint < state )
            {
                return;
            }

            _curPoint = state;
        }

        public void run( object state )
        {
            if ( _pc.isDead() )
            {
                return;
            }

            _regenPoint += _curPoint;
            _curPoint = 4;
            lock ( this )
            {
                if ( _regenMax <= _regenPoint )
                {
                    _regenPoint = 0;
                    regenHp();
                }
            }
        }

        public void updateLevel()
        {
            int[] lvlTable = new int[] { 30, 25, 20, 16, 14, 12, 11, 10, 9,
                        3, 2 };

            int regenLvl = Math.Min( 10, _pc.getLevel() );
            if ( 30 <= _pc.getLevel() && _pc.isKnight() )
            {
                regenLvl = 11;
            }

            lock ( this )
            {
                _regenMax = lvlTable[regenLvl - 1] * 4;
            }
        }

        public void regenHp()
        {
            if ( _pc.isDead() )
            {
                return;
            }

            int maxBonus = 1;

            // CONボーナス
            if ( 11 < _pc.getLevel() && 14 <= _pc.getCon() )
            {
                maxBonus = _pc.getCon() - 12;
                if ( 25 < _pc.getCon() )
                {
                    maxBonus = 14;
                }
            }

            int equipHpr = _pc.getInventory().hpRegenPerTick();
            equipHpr += _pc.getHpr();
            int bonus = _random.Next( maxBonus ) + 1;

            if ( _pc.hasSkillEffect( L1SkillId.NATURES_TOUCH ) )
            {
                bonus += 15;
            }
            if ( L1HouseLocation.isInHouse( _pc.getX(), _pc.getY(), _pc.getMapId() ) )
            {
                bonus += 5;
            }
            if ( _pc.getMapId() == 16384 || _pc.getMapId() == 16896
                    || _pc.getMapId() == 17408 || _pc.getMapId() == 17920
                    || _pc.getMapId() == 18432 || _pc.getMapId() == 18944
                    || _pc.getMapId() == 19968 || _pc.getMapId() == 19456
                    || _pc.getMapId() == 20480 || _pc.getMapId() == 20992
                    || _pc.getMapId() == 21504 || _pc.getMapId() == 22016
                    || _pc.getMapId() == 22528 || _pc.getMapId() == 23040
                    || _pc.getMapId() == 23552 || _pc.getMapId() == 24064
                    || _pc.getMapId() == 24576 || _pc.getMapId() == 25088 )
            { // 宿屋
                bonus += 5;
            }
            if ( ( _pc.getLocation().isInScreen( new Point( 33055, 32336 ) )
                    && _pc.getMapId() == 4 && _pc.isElf() ) )
            {
                bonus += 5;
            }
            if ( _pc.hasSkillEffect( L1SkillId.COOKING_1_5_N )
                    || _pc.hasSkillEffect( L1SkillId.COOKING_1_5_S ) )
            {
                bonus += 3;
            }
            if ( _pc.hasSkillEffect( L1SkillId.COOKING_2_4_N )
                    || _pc.hasSkillEffect( L1SkillId.COOKING_2_4_S )
                    || _pc.hasSkillEffect( L1SkillId.COOKING_3_6_N )
                    || _pc.hasSkillEffect( L1SkillId.COOKING_3_6_S ) )
            {
                bonus += 2;
            }
            if ( _pc.getOriginalHpr() > 0 )
            { // オリジナルCON HPR補正
                bonus += _pc.getOriginalHpr();
            }

            bool inLifeStream = false;
            if ( isPlayerInLifeStream( _pc ) )
            {
                inLifeStream = true;
                // 古代の空間、魔族の神殿ではHPR+3はなくなる？
                bonus += 3;
            }

            // 空腹と重量のチェック
            if ( _pc.get_food() < 3 || isOverWeight( _pc )
                    || _pc.hasSkillEffect( L1SkillId.BERSERKERS ) )
            {
                bonus = 0;
                // 装備によるＨＰＲ増加は満腹度、重量によってなくなるが、 減少である場合は満腹度、重量に関係なく効果が残る
                if ( equipHpr > 0 )
                {
                    equipHpr = 0;
                }
            }

            int newHp = _pc.getCurrentHp();
            newHp += bonus + equipHpr;

            if ( newHp < 1 )
            {
                newHp = 1; // ＨＰＲ減少装備によって死亡はしない
            }
            // 水中での減少処理
            // ライフストリームで減少をなくせるか不明
            if ( isUnderwater( _pc ) )
            {
                newHp -= 20;
                if ( newHp < 1 )
                {
                    if ( _pc.isGm() )
                    {
                        newHp = 1;
                    }
                    else
                    {
                        _pc.death( null ); // 窒息によってＨＰが０になった場合は死亡する。
                    }
                }
            }
            // Lv50クエストの古代の空間1F2Fでの減少処理
            if ( isLv50Quest( _pc ) && !inLifeStream )
            {
                newHp -= 10;
                if ( newHp < 1 )
                {
                    if ( _pc.isGm() )
                    {
                        newHp = 1;
                    }
                    else
                    {
                        _pc.death( null ); // ＨＰが０になった場合は死亡する。
                    }
                }
            }
            // 魔族の神殿での減少処理
            if ( _pc.getMapId() == 410 && !inLifeStream )
            {
                newHp -= 10;
                if ( newHp < 1 )
                {
                    if ( _pc.isGm() )
                    {
                        newHp = 1;
                    }
                    else
                    {
                        _pc.death( null ); // ＨＰが０になった場合は死亡する。
                    }
                }
            }

            if ( !_pc.isDead() )
            {
                _pc.setCurrentHp( Math.Min( newHp, _pc.getMaxHp() ) );
            }
        }

        private bool isUnderwater( L1PcInstance pc )
        {
            // ウォーターブーツ装備時か、 エヴァの祝福状態、修理された装備セットであれば水中では無いとみなす。
            if ( pc.getInventory().checkEquipped( 20207 ) )
            {
                return false;
            }
            if ( pc.hasSkillEffect( L1SkillId.STATUS_UNDERWATER_BREATH ) )
            {
                return false;
            }
            if ( pc.getInventory().checkEquipped( 21048 )
                    && pc.getInventory().checkEquipped( 21049 )
                    && pc.getInventory().checkEquipped( 21050 ) )
            {
                return false;
            }

            return pc.getMap().isUnderwater();
        }

        private bool isOverWeight( L1PcInstance pc )
        {
            // エキゾチックバイタライズ状態、アディショナルファイアー状態か
            // ゴールデンウィング装備時であれば、重量オーバーでは無いとみなす。
            if ( pc.hasSkillEffect( L1SkillId.EXOTIC_VITALIZE )
                    || pc.hasSkillEffect( L1SkillId.ADDITIONAL_FIRE ) )
            {
                return false;
            }
            if ( pc.getInventory().checkEquipped( 20049 ) )
            {
                return false;
            }

            return ( 120 <= pc.getInventory().getWeight240() ) ? true : false;
        }

        private bool isLv50Quest( L1PcInstance pc )
        {
            int mapId = pc.getMapId();
            return ( mapId == 2000 || mapId == 2001 ) ? true : false;
        }

        /// <summary>
        /// 指定したPCがライフストリームの範囲内にいるかチェックする。
        /// </summary>
        /// <param name="pc"></param>
        /// <returns>Cがライフストリームの範囲内にいる場合</returns>
        private static bool isPlayerInLifeStream( L1PcInstance pc )
        {
            foreach ( L1Object l1object in pc.getKnownObjects() )
            {
                if ( l1object is L1EffectInstance == false )
                {
                    continue;
                }
                L1EffectInstance effect = (L1EffectInstance)l1object;
                if ( effect.getNpcId() == 81169 && effect.getLocation()
                        .getTileLineDistance( pc.getLocation() ) < 4 )
                {
                    return true;
                }
            }
            return false;
        }
    }

}
